//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows.Media;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Formats.Biff.Records;
using OpenLS.Spreadsheet.Formats.Biff.Tokens;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet.Formats.Biff
{
#if DEBUG
    public
#endif
        sealed class BiffReaderWorkbookContext
    {

        private MsoDrawingGroupRecord _drawingGroup;

        internal MsoDrawingGroupRecord DrawingGroup
        {
            get { return _drawingGroup; }
            set
            {
                Contract.Assert(DrawingGroup == null);
                _drawingGroup = value;
            }
        }

        //private readonly List<Action<object>> _actions = new List<Action<object>>();
        private readonly List<BoundSheetRecord> _boundSheets = new List<BoundSheetRecord>();
        //private readonly List<Action<object>> _definedNameActions = new List<Action<object>>();
        private readonly List<DefinedName> _definedNames = new List<DefinedName>();
        private readonly List<ExternalBookContext> _externalBookContexts = new List<ExternalBookContext>();
        private readonly List<ExternalReferencedSheet> _externalSheets = new List<ExternalReferencedSheet>();

        private readonly Dictionary<int, Font> _fontTable = new Dictionary<int, Font>();
        private readonly Dictionary<int, string> _formatTable = new Dictionary<int, string>();
        private readonly ArrayIntDictionary<CellFormattingRecord> _cellFormattingRecords = new ArrayIntDictionary<CellFormattingRecord>(v => v == null, null);
        private readonly ArrayIntDictionary<StyleFormattingRecord> _styleFormattingRecords = new ArrayIntDictionary<StyleFormattingRecord>(v => v == null, null);
        //private readonly List<Action<object>> _resolveNameActions = new List<Action<object>>();
        //private readonly List<Action<object>> _resolveExternalNameActions = new List<Action<object>>();
        //private readonly List<Action<object>> _sharedFormulaActions = new List<Action<object>>();
        // private readonly List<Action<object>> _sheetActions = new List<Action<object>>();
        private readonly ArrayIntDictionary<int> _stringIdToStringIndexTable = new ArrayIntDictionary<int>(i => i == -1, -1);
        private readonly List<Cell> _toRecalculate = new List<Cell>();
        private readonly Workbook _workbook;

        public BiffReaderWorkbookContext(Workbook workbook)
        {
            _workbook = workbook;
        }

        internal ExternalBookContext CurrentExternalBookContext
        {
            get { return _externalBookContexts[_externalBookContexts.Count - 1]; }
        }

        public List<DefinedName> DefinedNames
        {
            get { return _definedNames; }
        }

        internal List<BoundSheetRecord> BoundSheets
        {
            get { return _boundSheets; }
        }

//        public List<Action<object>> Actions
        //      {
        //        get { return _actions; }
        //  }

//        public List<Action<object>> SharedFormulaActions
        //      {
        //        get { return _sharedFormulaActions; }
        //  }

        //  public List<Action<object>> DefinedNameActions
//        {
//            get { return _definedNameActions; }
        //}

        internal List<Action<object>> GetActions(ActionType type)
        {
            List<Action<object>> result = _allActions[(int) type];
            if (result == null)
            {
                result = new List<Action<object>>();
                _allActions[(int) type] = result;
            }
            return result;
        }

        /* public List<Action<object>> SheetActions
        {
            get { return _sheetActions; }
        }*/
        private readonly List<Action<object>>[] _allActions = new List<Action<object>>[(int) ActionType.Last];

//        public List<Action<object>> ResolveNameActions
        //      {
        //        get { return _resolveNameActions; }
        //  }

//        public List<Action<object>> ResolveExternalNameActions
        //      {
        //        get { return _resolveExternalNameActions; }
        //  }

        public List<Cell> ToRecalculate
        {
            get { return _toRecalculate; }
        }

        public Workbook Workbook
        {
            get { return _workbook; }
        }

        public Dictionary<int, string> FormatTable
        {
            get { return _formatTable; }
        }

        /*internal Dictionary<int, CellFormattingRecord> RecordTable //\\ rename
        {
            get { return _recordTable; }
        }*/

        private Dictionary<int, Font> FontTable
        {
            get { return _fontTable; }
        }

        internal Font GetFont (int fid)
        {
            Contract.Requires(fid != 4);
            if (fid > 4)
                fid--;
            return FontTable[fid];
        }
        public void AddFont (Font font)
        {
            FontTable.Add(FontTable.Count, font);

        }

        internal List<ExternalBookContext> ExternalBookContexts
        {
            get { return _externalBookContexts; }
        }

        internal ArrayIntDictionary<int> StringIdToStringIndexTable
        {
            get { return _stringIdToStringIndexTable; }
        }

        internal List<ExternalReferencedSheet> ExternalSheets
        {
            get { return _externalSheets; }
        }

        internal string GetExternalDefinedName(int xti, int nameIndex)
        {
            Contract.Requires(xti >= 0);
            var  book = this._externalSheets[xti].Book;
            var ctx = Enumerable.First(_externalBookContexts, c => c.Workbook == book);
            //var book = _externalBookContexts[externalBookIndex];)
            return ctx.DefinedNames[nameIndex];
        }

        public int ExternalBookCount     
        {
            get { return _externalBookContexts.Count; }
        }

        internal IndexedColor GetColor(IcvFont index)
        {
            var result = new IndexedColor(GetColorImp((ushort)index), (ushort)index);
            return result;
        }
        internal IndexedColor GetColor(IcvXF index, IcvXF nullIndex)
        {
            if (index == nullIndex)
                return null;
            var result = new IndexedColor(GetColorImp((byte)index), (byte)index);
            return result;
        }
        internal IndexedColor GetColor(IcvXF index)
        {
            var result = new IndexedColor(GetColorImp((byte)index), (byte)index);
            return result;
        }
        private Color GetColorImp(int index)
        {
          //  if (this.palette.ContainsIndex(index))
            //    return palette.GetColor(index);
            // Debug.WriteLine(string.Format("Getting color for {0:X} {1}", index, Workbook.StyleSheet.GetIndexedColor(index)));
            return Workbook.StyleSheet.GetIndexedColor(index);
        }

        internal Palette palette = new BuiltinPalette();
        private readonly List<Range> _autoFilters = new List<Range>();
        public ushort WorksheetIndex;

        internal Worksheet GetExternalSheet(ushort index)
        {
            ExternalReferencedSheet obj = ExternalSheets[index];
            Workbook ex = obj.Book;
            if (obj.FirstTabIndex == 0xFFFF)
                return null;
            return (Worksheet) ex.Sheets[obj.FirstTabIndex];
        }


        internal Range GetAutofilter(Worksheet worksheet)
        {
            return Enumerable.First(_autoFilters, r => r.Worksheet == worksheet);
        }

        internal void AddFilterDatabase(Expression expression)
        {
            var r = expression.GetObject(new WorkbookEvaluationContext(_workbook));
            if (r is Error)
                return;
            Range range = (Range) r;
            _autoFilters.Add(range);
        }

        internal StyleFormattingRecord GetStyleFormattingRecord(int xfId)
        {
            Contract.Assert(_styleFormattingRecords.Count == Enumerable.Count(Enumerable.Distinct(_styleFormattingRecords.Values)));
            return _styleFormattingRecords[xfId];
        }

        internal CellFormattingRecord GetCellFormattingRecord(int xfId)
        {
            CellFormattingRecord result;
            _cellFormattingRecords.TryGetValue(xfId, out result);
            return result;
        }

        internal void AddCellFormattingRecord(CellFormattingRecord sr)
        {
            Contract.Requires(sr != null);
            var totalCount = _styleFormattingRecords.Count + _cellFormattingRecords.Count;

            this._cellFormattingRecords.Add(totalCount, sr);
        }

        internal void AddStyleFormattingRecord(StyleFormattingRecord sr)
        {
            Contract.Requires(sr != null);
            Contract.Assert(_styleFormattingRecords.Count == Enumerable.Count(Enumerable.Distinct(_styleFormattingRecords.Values)));

            var totalCount = this._styleFormattingRecords.Count + this._cellFormattingRecords.Count;
            this._styleFormattingRecords.Add(totalCount, sr);
            Contract.Assert(_styleFormattingRecords.Count == Enumerable.Count(Enumerable.Distinct(_styleFormattingRecords.Values)));

        }

        public bool ContainsAutoFilter(Worksheet worksheet)
        {
            return Enumerable.Any(_autoFilters, f => f.Worksheet == worksheet);
        }

        /*internal void SetStyleFormattingRecord(StyleFormattingRecord styleFormattingRecord, ushort xfId)
        {
            Contract.Assert(_styleFormattingRecords.ContainsKey(xfId));
            _styleFormattingRecords.Remove(xfId);
            this._styleFormattingRecords.Add(xfId, styleFormattingRecord);
        }*/

        public DefinedName GetDefinedName(int i)
        {
            return this._definedNames[i];
            return Workbook.DefinedNames[i];
        }
    }
}